package x.ovo.wechat.bot.event;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadUtil;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import x.ovo.wechat.bot.common.Context;
import x.ovo.wechat.bot.common.entity.message.Message;
import x.ovo.wechat.bot.common.event.Event;
import x.ovo.wechat.bot.common.event.EventListener;
import x.ovo.wechat.bot.common.event.EventManager;
import x.ovo.wechat.bot.common.plugin.Plugin;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 默认事件管理器
 *
 * @author ovo, created by 2024/05/01
 */
@Slf4j(topic = "事件管理器")
@SuppressWarnings({"unchecked", "rawtypes"})
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class DefaultEventManager implements EventManager {

    @Getter
    private final Context context;
    private final Map<Plugin, List<EventListener>> listeners = MapUtil.newConcurrentHashMap();
    private ExecutorService executor;

    public static EventManager create(Context context) {
        DefaultEventManager manager = new DefaultEventManager(context);
        context.setEventManager(manager);
        manager.executor = ExecutorBuilder.create()
                .setCorePoolSize(5)
                .setMaxPoolSize(20)
                .setThreadFactory(ThreadUtil.newNamedThreadFactory("event-listener-", true))
                .setWorkQueue(new LinkedBlockingQueue<>(50))
                .build();
        log.info("事件管理器初始化完成");
        return manager;
    }


    @Override
    public void register(@NonNull Plugin plugin, EventListener<? extends Event<?>> listener) {
        if (Objects.isNull(listener)) return;
        listener.setContext(this.context);
        listener.setPlugin(plugin);
        this.listeners.computeIfAbsent(plugin, k -> new ArrayList<>()).add(listener);
    }

    @Override
    public void unregister(@NonNull Plugin plugin) {
        this.listeners.remove(plugin);
    }

    @Override
    public void callEvent(@NonNull Event<?> event) {
        event.setContext(this.context);
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        AtomicReference<ClassLoader> classLoader = new AtomicReference<>();
        this.listeners.entrySet().stream()
                // 插件是否启用
                .filter(entry -> entry.getKey().isEnabled())
                // 如果事件源是消息，判断是否是群消息，如果是群，则判断该群是否启用插件
                .filter(entry -> {
                    if (event.getSource() instanceof Message message && message.isGroup()) {
                        return this.context.getPluginManager().isGroupEnabled(message.getFromUser().getNickName(), entry.getKey());
                    }
                    return true;
                })
                // 获取插件的类加载器
                .peek(entry -> classLoader.set(entry.getKey().getClassLoader()))
                // 获取插件的监听器
                .flatMap(entry -> entry.getValue().stream())
                // 筛选出支持该事件的监听器
                .filter(l -> l.support(event))
                // 并执行
                .forEach(l -> this.executor.execute(() -> {
                    Thread.currentThread().setContextClassLoader(classLoader.get());
                    try {
                        l.onEvent(event);
                    } catch (Exception e) {
                        log.error("事件监听器执行异常", e);
                    } finally {
                        Thread.currentThread().setContextClassLoader(originalClassLoader); // 恢复原类加载器
                    }
                }));
    }
}
